using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace DotNetCommon.Expressions.Base
{
    internal class BaseVisit
    {
        /// <summary>
        /// 生成当前节点的字符串描述
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        protected virtual string GenerateFullMarkString(ExpressionNode node) => throw new NotImplementedException();
        /// <summary>
        /// 准备树形结构
        /// </summary>
        /// <param name="node"></param>
        /// <exception cref="NotImplementedException"></exception>
        protected virtual void Prepare(ExpressionNode node) => throw new NotImplementedException();
        /// <summary>
        /// 重新生成当前节点(当子节点有简化动作时,父节点要冒泡一样一级级跟着Rebuild)
        /// </summary>
        /// <param name="node"></param>
        /// <remarks>注意：简化后不能更改当前节点的类型,比如: [bool? ok;p=>ok??p.Id>1;] 整体是bool,Left是bool?, 简化后虽然使用了[ok]的值, 但整体类型应仍然是[bool]</remarks>
        /// <returns></returns>
        internal virtual Expression Rebuild(ExpressionNode node) => node.Expression;

        /// <summary>
        /// 简化表达式树入口
        /// </summary>
        /// <param name="node"></param>
        /// <param name="visit"></param>
        /// <param name="isKeepCallBack">是否保留此表达式,比如: 在 根据lambda解析生成update语句的时候,期望保留 new Person{}</param>
        /// <param name="isDisableReduce">是否禁止精简,用在 ExpressionHelper.GetFullMarkString 中</param>
        internal void Reduce(ExpressionNode node, Action<ExpressionNode> visit, Func<Expression, bool> isKeepCallBack, bool isDisableReduce = false)
        {
            Prepare(node);
            node.Children.ForEach(x => visit(x));
            //node.FullMarkString = GenerateFullMarkString(node);
            if (!isDisableReduce)
            {
                var isKeep = false;
                if (isKeepCallBack != null) isKeep = isKeepCallBack(node.Expression);
                SimpleReduce(node, isKeep);
                if (!isKeep) Reduce2(node);
            }
            //延迟生成当前节点的描述,因为在简化中用不到当前节点的
            node.FullMarkString = ExpressionHelper.GetVisit(node.NodeType.Value).GenerateFullMarkString(node);

        }
        /// <summary>
        /// 在SimpleReduce基础上再进行一次简化,处理短路的运算符,比如: lambda[p=>true||p.IsWrite] 会简化为 Constant[true]
        /// </summary>
        /// <param name="node"></param>
        protected virtual void Reduce2(ExpressionNode node) { }

        /// <summary>
        /// 通用节点简化方法,简化条件:
        /// <list type="bullet">
        /// <item>当前节点必须有两个或以上的子节点;</item>
        /// <item>子节点中必须同时存在含参数的节点和不含参数的节点;</item>
        /// </list>
        /// 这样, 不含参数的子节点将被简化, 如果所有子节点都不含参数, 那说明简化的时机还没到, 延迟到父节点进行判断
        /// </summary>
        /// <param name="node"></param>
        /// <param name="isKeep"></param>
        private void SimpleReduce(ExpressionNode node, bool isKeep)
        {
            //表示当前节点是否是断点,如果是的话要尽量在这里执行简化操作,否则将简化操作延迟到父级
            var isBreakPoint = false;

            //先不考虑用户指定的 isKeep
            //为每个子节点计算是否含有参数
            bool hasParameterFlag = false;//是否有子节点含有参数
            bool noParameterFlag = false;//是否有子节点不含参数
            bool hasEvalFlag = false;//是否有子节点含有Eval()标记
            var hasEvalFlags = new bool[node.Children.Count];//所有子节点的Eval()标记
            var parameters = node.GetParameters(EnumParameterTreeType.AllParentIncludeSelf);
            for (int i = 0; i < node.Children.Count; i++)
            {
                var child = node.Children[i];
                var flag = child.HasParameter = child.hasParameter(parameters);
                if (flag) hasParameterFlag = true;
                else noParameterFlag = true;
                if (child.Expression is MethodCallExpression call && call.Method.GetMethodGenericFullName().Name == "T DotNetCommon.Extensions.GenericExtensions.Eval<T>(T obj)")
                    hasEvalFlag = hasEvalFlags[i] = true;
            }

            //isBreakPoint: 决定是否在此节点对子节点执行简化操作
            if (hasParameterFlag && noParameterFlag)
            {
                //子节点中同时具有含参数的和不含参数的 要进行简化(此时不需要用户指定 isKeep)
                isBreakPoint = true;
                //虽然此时用户可能也指定了 isKeep 但是不需要将当前节点标记为 FakeParameter
            }
            else
            {
                //正常情况下 不需要在此节点对子节点进行简化
                if (isKeep)
                {
                    //用户逻辑指定保留当前节点并且有可以简化的子节点,那么在直接在此节点进行简化操作
                    isBreakPoint = true;
                    //将自身标记为 parameter 防止被上级简化
                    node.IsParameter = true;
                    node.HasParameter = true;
                    node.FakeParameter = Expression.Parameter(typeof(object));
                    node.GetParameters(EnumParameterTreeType.RootParent).Add(node.FakeParameter);
                }
                else if (node.NodeType == ExpressionType.Lambda && noParameterFlag)
                {
                    //lambda表达式肯定要尝试简化子节点
                    //lambda表达式无法在父节点上被简化,最多将它的body简化成常量
                    //场景1: lambda = i => 1+2; 应该被简化成: i => 3
                    isBreakPoint = true;
                }
                else if (hasEvalFlag)
                {
                    isBreakPoint = true;
                }
            }
            if (isBreakPoint)
            {
                //进行简化
                for (int i = 0; i < node.Children.Count; i++)
                {
                    var child = node.Children[i];
                    if ((!child.HasParameter || hasEvalFlags[i]) && child.Expression != null)
                    {
                        if (child.NodeType == ExpressionType.Lambda || child.NodeType == ExpressionType.Quote || child.NodeType == ExpressionType.Parameter)
                        {
                            //lambda表达式和quote无法被简化, 见 ExpressionNode.cs 中的分析
                            continue;
                        }
                        else if (child.NodeType != ExpressionType.Constant)
                        {
                            //进行简化
                            var res = child.Reduce();
                            var para = Expression.Parameter(child.Expression.Type);
                            node.outMidValues.Add(para, res);
                            node.Children[i] = new ExpressionNode
                            {
                                Parent = node,
                                Expression = para,
                                _updateRequest = true,
                                FullMarkString = child.Expression.Type.GetClassFullName()
                            };
                        }
                    }
                }
            }
            //如果子孙节点有更新 则 rebuid 当前节点
            if (node.NeedUpdate) node.Expression = Rebuild(node);
        }

        /// <summary>
        /// 获取表达式树访问的参数属性名称
        /// </summary>
        /// <param name="node"></param>
        /// <param name="visit"></param>
        /// <param name="parameters"></param>
        /// <param name="names"></param>
        internal virtual void GetAccessName(ExpressionNode node, Action<ExpressionNode> visit, List<ParameterExpression> parameters, List<List<string>> names)
        {
            Prepare(node);
            node.Children.ForEach(x => visit(x));
            if (node.NodeType == ExpressionType.MemberAccess)
            {
                var member = node.Expression as MemberExpression;
                if (member.Expression.NodeType == ExpressionType.Parameter)
                {
                    var index = parameters.IndexOf(member.Expression as ParameterExpression);
                    if (index >= 0)
                    {
                        names[index].Add(member.Member.Name);
                    }
                }
            }
        }
    }
}
